package org.sibframework.sda.manager;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.sibframework.sda.SimpleDataAccess;

public class ModelManagerFactory {
	private static boolean useCache = false;
	//private static Set<Package> packages = new HashSet<Package>();
	private static Map<Class<?>, Class<IModelManager<?>>> registry = new HashMap<Class<?>, Class<IModelManager<?>>>();
	//private static Map<Class<?>, IModelManager<?>> cache = new HashMap<Class<?>, IModelManager<?>>();
	private static IDefaultModelManagerCreator defaultModelManagerCreator = new IDefaultModelManagerCreator(){
		@Override
		public <T> IModelManager<T> createDefaultModelManager(Class<T> clazz) throws SQLException {
			return new ModelManager<T>(clazz);
		}};
	
	static {
		init();
	}

	private static void init() {
	}
	

	public static void setDefaultModelManagerCreator(IDefaultModelManagerCreator defaultMC) {
		defaultModelManagerCreator = defaultMC;
	}


	public static synchronized <T> IModelManager<T> findModelManager(Class<T> clazz) throws SQLException {
		return findTransactionModelManager(clazz, null);
	}
	
	
	
	
	@SuppressWarnings("unchecked")
	public static synchronized <T> IModelManager<T> findTransactionModelManager(Class<T> clazz, SimpleDataAccess sda) throws SQLException {
		//ignore cache;
	
		IModelManager<T> mm = null;
		
		Class<IModelManager<?>> result = registry.get(clazz);
		if (result != null) {
			try {
				mm = (IModelManager<T>) result.newInstance();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		} else if (defaultModelManagerCreator != null) {
			mm = defaultModelManagerCreator.createDefaultModelManager(clazz);
		} else {
			mm = new ModelManager<T>(clazz);
		}
		
		if (sda != null) {
			mm.setSDA(sda);
		}
		return mm;
	}

	public static void feedManager(Object object) {
		for (PropertyDescriptor pd : PropertyUtils.getPropertyDescriptors(object)) {
			if (IModelManager.class.isAssignableFrom(pd.getPropertyType())) {
				try {

					Type returnType  = pd.getReadMethod().getGenericReturnType();
					if  (returnType  instanceof  ParameterizedType) {
			            Type type  =  ((ParameterizedType)returnType).getActualTypeArguments()[0];
			            IModelManager<?> manager = ModelManagerFactory.findModelManager((Class<?>) type);
			            pd.getWriteMethod().invoke(object, manager);
			        }
					
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
		}
	}
	

	@SuppressWarnings("unchecked")
	public static void regist(String classStr, Class<?> modelClass) {
		Class<IModelManager<?>> managerClass;
		try {
			managerClass = (Class<IModelManager<?>>) Class.forName(classStr);
			registry.put(modelClass, managerClass);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	
	@SuppressWarnings({"rawtypes" })
	public static void regist(Class managerClass, Class<?> modelClass) {
		regist(managerClass.getName(), modelClass);
	}
	
	
	public static void regist(Class<IModelManager<?>> clazz) {
		if (clazz.isInterface()) {
			return;
		}
		if (IModelManager.class.isAssignableFrom(clazz)) {
			try {
				Class<?> modelClass = clazz.getMethod("getById", new Class[]{Serializable.class}).getReturnType();
				//Class modelClass = (Class) ((ParameterizedType) clazz.getGenericInterfaces()[0]).getActualTypeArguments()[0];
				registry.put(modelClass, clazz);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}


	/**
	 * @Deprecated not support cache any more.
	 */
	public static boolean isUseCache() {
		return useCache;
	}


	/**
	 * @Deprecated not support cache any more.
	 * @param useCache
	 */
	public static void setUseCache(boolean useCache) {
		ModelManagerFactory.useCache = useCache;
		
	}
	
/*	public static void regist(List<ModelItem> mis) {
		for (ModelItem mi : mis) {
			if (mi.getManager()!= null && mi.getManager().length() > 0) {
				try {
					Class<IModelManager> managerClass = (Class<IModelManager>) Class.forName(mi.getManager());
					Class<?> modelClass = Class.forName(mi.getPackageName());
					regist(managerClass, modelClass);
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
	}*/
	
}